#include "SQLMethodInvokeExpr.h"
#include "../../../utils/FnvHash.h"
#include "../../visitor/SQLASTVisitor.h"
#include "../../../sql/dialect/oracle/visitor/OracleASTVisitor.h"
#include "../../../utils/listTran.h"
#include "../../../utils/log.h"
#include "../../../utils/instanceof.h"

SETCALSS(SQLMethodInvokeExpr);

SQLMethodInvokeExpr::SQLMethodInvokeExpr() : owner(NULL)
{
  parameters = std::make_shared<std::list<SQLExpr_ptr>>();
  arguments = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLMethodInvokeExpr::~SQLMethodInvokeExpr()
{
  // for (std::list<SQLExpr_ptr >::iterator it = parameters->begin(); it != parameters->end(); ++it)
  // {
  //   delete *it;
  // }
  // parameters->clear();

  if (owner != NULL)
  {
    // delete owner;
    owner = NULL;
  }
}
SQLMethodInvokeExpr::SQLMethodInvokeExpr(string_ptr theMethodName)
    : owner(NULL), methodName(theMethodName)
{
  parameters = std::make_shared<std::list<SQLExpr_ptr>>();
  arguments = std::make_shared<std::list<SQLExpr_ptr>>();
  // methodName = theMethodName;
}

SQLMethodInvokeExpr::SQLMethodInvokeExpr(string_ptr theMethodName, SQLExpr_ptr theOwner)
    : methodName(theMethodName), owner(theOwner)
{
  parameters = std::make_shared<std::list<SQLExpr_ptr>>();
  arguments = std::make_shared<std::list<SQLExpr_ptr>>();
  // this.methodName = methodName;
  // setOwner(owner);
}

string_ptr SQLMethodInvokeExpr::getMethodName()
{
  return methodName;
}

SQLExpr_ptr SQLMethodInvokeExpr::getOwner()
{
  return owner;
}

void SQLMethodInvokeExpr::setOwner(SQLExpr_ptr theOwner)
{
  if (theOwner != NULL)
  {
    theOwner->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  owner = theOwner;
}

SQLExpr_list_ptr SQLMethodInvokeExpr::getParameters()
{
  return parameters;
}

SQLExpr_list_ptr SQLMethodInvokeExpr::getArguments()
{
  return arguments;
}

void SQLMethodInvokeExpr::addArgument(SQLExpr_ptr arg)
{
  if (arg != NULL)
  {
    arg->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  this->arguments->push_back(arg);
}

void SQLMethodInvokeExpr::addParameter(SQLExpr_ptr theParam)
{
  if (theParam != NULL)
  {
    theParam->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  parameters->push_back(theParam);
}

void SQLMethodInvokeExpr::output(std::stringstream *buf)
{
  if (owner != NULL)
  {
    owner->output(buf);
    (*buf) << ".";
  }

  (*buf) << *methodName;
  (*buf) << "(";

  // for (int i = 0, size = parameters.size(); i < size; ++i) {
  // if (i != 0) {
  // buf.append(", ");
  //}

  // this.parameters.get(i).output(buf);
  //}

  for (std::list<SQLExpr_ptr>::iterator it = parameters->begin(); it != parameters->end(); ++it)
  {

    (*buf) << ", ";
    (*it)->output(buf);
  }

  (*buf) << ")";
}

void SQLMethodInvokeExpr::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLMethodInvokeExpr)))
  {
    acceptChild(visitor, owner);
    SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(parameters);
    acceptChild(visitor, tmp);
  }

  visitor->endVisit(SharedObject(SQLMethodInvokeExpr));
}

void SQLMethodInvokeExpr::accept0(std::shared_ptr<OracleASTVisitor> visitor)
{
  if (visitor->SQLASTVisitor::visit(SharedObject(SQLMethodInvokeExpr)))
  {
    acceptChild(visitor, owner);
    SQLObject_list_ptr tmp = ListTran::tran_class<SQLExpr, SQLObject>(parameters);
    acceptChild(visitor, tmp);
  }

  visitor->SQLASTVisitor::endVisit(SharedObject(SQLMethodInvokeExpr));
}

// int SQLMethodInvokeExpr::hashCode() {
// final int prime = 31;
// int result = 1;
// result = prime * result + ((methodName == NULL) ? 0 : methodName.hashCode());
// result = prime * result + ((owner == NULL) ? 0 : owner.hashCode());
// result = prime * result + ((parameters == NULL) ? 0 : parameters.hashCode());
// return result;
//}

// BOOL_ptr SQLMethodInvokeExpr::equals(Object_ptr obj) {
// if (this == obj) {
// return BOOL::TRUE;
//}
// if (obj == NULL) {
// return BOOL::FALSE;
//}
// if (getClass() != obj.getClass()) {
// return BOOL::FALSE;
//}
// SQLMethodInvokeExpr other = (SQLMethodInvokeExpr) obj;
// if (methodName == NULL) {
// if (other.methodName != NULL) {
// return BOOL::FALSE;
//}
//} else if (!methodName.equals(other.methodName)) {
// return BOOL::FALSE;
//}
// if (owner == NULL) {
// if (other.owner != NULL) {
// return BOOL::FALSE;
//}
//} else if (!owner.equals(other.owner)) {
// return BOOL::FALSE;
//}
// if (parameters == NULL) {
// if (other.parameters != NULL) {
// return BOOL::FALSE;
//}
//} else if (!parameters.equals(other.parameters)) {
// return BOOL::FALSE;
//}
// return BOOL::TRUE;
//}

long SQLMethodInvokeExpr::methodNameHashCode64()
{
  if (methodNameHashCode64_ == 0 && !methodName->empty())
  {
    methodNameHashCode64_ = FnvHash::hashCode64(methodName);
  }
  return methodNameHashCode64_;
}

void SQLMethodInvokeExpr::setMethodName(string_ptr methodName)
{
  this->methodName = methodName;
  this->methodNameHashCode64_ = 0L;
}

SQLExpr_ptr SQLMethodInvokeExpr::getFrom()
{
  return from;
}

void SQLMethodInvokeExpr::setFrom(SQLExpr_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  this->from = x;
}

SQLExpr_ptr SQLMethodInvokeExpr::getUsing()
{
  return using_;
}

void SQLMethodInvokeExpr::setUsing(SQLExpr_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  this->using_ = x;
}

SQLExpr_ptr SQLMethodInvokeExpr::getFor()
{
  return hasFor;
}

void SQLMethodInvokeExpr::setFor(SQLExpr_ptr x)
{
  if (x != nullptr)
  {
    x->setParent(SharedObject(SQLMethodInvokeExpr));
  }
  this->hasFor = x;
}

SQLDataType_ptr SQLMethodInvokeExpr::getResolvedReturnDataType()
{
  return resolvedReturnDataType;
}

void SQLMethodInvokeExpr::setResolvedReturnDataType(SQLDataType_ptr resolvedReturnDataType)
{
  this->resolvedReturnDataType = resolvedReturnDataType;
}

void SQLMethodInvokeExpr::cloneTo(SQLMethodInvokeExpr_ptr x)
{
  x->methodName = methodName;

  if (owner != nullptr)
  {
    x->setOwner(std::dynamic_pointer_cast<SQLExpr>(owner->clone()));
  }

  for (SQLExpr_ptr arg : *arguments)
  {
    x->addArgument(std::dynamic_pointer_cast<SQLExpr>(arg->clone()));
  }

  if (from != nullptr)
  {
    x->setFrom(std::dynamic_pointer_cast<SQLExpr>(from->clone()));
  }

  if (using_ != nullptr)
  {
    x->setUsing(std::dynamic_pointer_cast<SQLExpr>(using_->clone()));
  }

  if (trimOption != nullptr)
  {
    x->setTrimOption(trimOption);
  }

  if (attributes != nullptr)
  {
    for (auto entry : *attributes)
    {
      std::string key = entry.first;
      Object_ptr value = entry.second;
      if (instanceof <Object, SQLObject>(value))
      {
        value = (std::dynamic_pointer_cast<SQLObject>(value))->clone();
      }
      x->putAttribute(key, value);
    }
  }
}

string_ptr SQLMethodInvokeExpr::getTrimOption()
{
  return trimOption;
}

void SQLMethodInvokeExpr::setTrimOption(string_ptr trimOption)
{
  this->trimOption = trimOption;
}